package ufmg.maze;

import java.util.Random;
import java.util.Set;
import java.util.TreeSet;
import java.util.Vector;

import ufmg.maze.aco.Formiga;

public class Maze {

	// remove paredes cima, baixo, esquerda, direita
	private Vector<Vector<Integer>> maze;

	private int inicial_coluna; // comecando em 1
	private int inicial_linha; // comecando em 0

	private int saida_coluna; // comecando em 1
	private int saida_linha; // comecando em 0

	public static final int MAZE_VAZIO = 0;
	public static final int MAZE_PAREDE = 1;
	public static final int MAZE_MAIS = 7;
	
	private double[][] feromonio;
	
	private Set<Integer>[][] visitantes;

	private int tamanho; // casas livres do maze
	
	private Random rand = new Random();

	public Maze() {
		this.maze = new Vector<Vector<Integer>>();
	}
	
	public void criarMatrizFeromonio()
	{
		feromonio = new double[getLimiteY()][getLimiteX()];
		visitantes = new TreeSet[getLimiteY()][getLimiteX()];
	}
	
	public void adicionaVisitante(Formiga f, int i, int j)
	{
		visitantes[i][j].add(f.getIdentificador());
	}
	
	public boolean jaVisitou(Formiga f, int i, int j)
	{
		return visitantes[i][j].contains(f.getIdentificador());
	}
	
	public void transformaEmParede(int i, int j)
	{
		if(i>0)
		{
			maze.get(i-1).set(j, MAZE_PAREDE);
		}
		if(i<getLimiteY()-1)
			maze.get(i+1).set(j, MAZE_PAREDE);
		if(j>0)
		{
			maze.get(i).set(j-1, MAZE_PAREDE);
		}
		if(j<getLimiteX()-1)
			maze.get(i).set(j+1, MAZE_PAREDE);
		
	}
	
	public double getFeromonio(int i, int j)
	{
		return feromonio[i][j];
	}
	
	public void setFeromonio(int i, int j, double v)
	{
		feromonio[i][j] = v;
	}

	public void addLinhaCorredor(String[] linha) {
		int colunaCountOriginal = 0;

		Vector<Integer> mazeLinha = new Vector<Integer>();
		for (String token : linha) {
			if (token.length() > 0) {

				int posicao = token.indexOf("S");
				if (posicao > -1) {
					inicial_coluna = colunaCountOriginal + 2
							* ((posicao - 1) / 4 + 1) - 1;
					inicial_coluna--;

					inicial_linha = maze.size() - 1;

					System.out.println("Inicial: " + inicial_linha + ","
							+ inicial_coluna);
				}

				posicao = token.indexOf("E");
				if (posicao > -1) {
					saida_coluna = colunaCountOriginal + 2
							* ((posicao - 1) / 4 + 1) - 1;
					saida_coluna--;
					saida_linha = maze.size() - 1;
					System.out.println("Final: " + saida_linha + ","
							+ saida_coluna);
				}

				int corredorSize = 1 + ((token.length() - 3) / 4);
				// tamanho += corredorSize;

				// System.out.println("#" + token + "#" + corredorSize);
				for (int i = 0; i < corredorSize; i++) {
					tamanho++;
					mazeLinha.add(MAZE_VAZIO);
					colunaCountOriginal++;
					if (i < corredorSize - 1) {
						mazeLinha.add(MAZE_MAIS);
						colunaCountOriginal++;
					}
				}
				mazeLinha.add(MAZE_PAREDE);
				colunaCountOriginal++;
				// System.out.println(colunaCountOriginal);
			}
		}
		mazeLinha.remove(mazeLinha.size() - 1);
		maze.add(mazeLinha);
	}
	
	
	public int obtemValorCaminhoFeromonio()
	{
		int tamanho = 0;
		
		int x = getInicialColuna();
		int y = getInicialLinha();
		
		double f[] = new double[4];
		
		int maior = Formiga.LESTE;
		int anterior = -1;
		
		do
		{
			f[0] = isValidMove(y, x, Formiga.NORTE) && anterior!=Formiga.SUL? feromonio[y-2][x] : 0;
			f[1] = isValidMove(y, x, Formiga.SUL) && anterior!=Formiga.NORTE? feromonio[y+2][x] : 0;
			f[2] = isValidMove(y, x, Formiga.LESTE) && anterior!=Formiga.OESTE? feromonio[y][x+2] : 0;
			f[3] = isValidMove(y, x, Formiga.OESTE) && anterior!=Formiga.LESTE? feromonio[y][x-2] : 0;
			
			if(f[Formiga.LESTE]>f[maior]) maior = Formiga.LESTE;
			else if(f[Formiga.OESTE]>f[maior]) maior = Formiga.OESTE;
			else if(f[Formiga.SUL]>f[maior]) maior = Formiga.SUL;
			else if(f[Formiga.NORTE]>f[maior]) maior = Formiga.NORTE;
			
			if(maior==Formiga.NORTE) y-=2;
			else if(maior==Formiga.SUL) y+=2;
			else if(maior==Formiga.LESTE) x+=2;
			else if(maior==Formiga.OESTE) x-=2;
			
			anterior = maior;
			tamanho++;
		
		} while(f[maior]>0);
		return tamanho;
	}



	
	public byte nextPosition(int i, int j)
	{
		double[] prob = new double[4];
		
		prob[0] = isValidMove(i, j, Formiga.OESTE)? feromonio[i][j-2] + 0.25 : 0;
		prob[1] = isValidMove(i, j, Formiga.LESTE)? feromonio[i][j+2] + 0.25 : 0;
		prob[2] = isValidMove(i, j, Formiga.NORTE)? feromonio[i-2][j] + 0.25 : 0;
		prob[3] = isValidMove(i, j, Formiga.SUL)? feromonio[i+2][j] + 0.25 : 0;
		
		double F = 0;
		for(int ind = 0; ind<prob.length; ind++)
		{
			F+=prob[ind];
		}
		
		double p = rand.nextDouble();
		double li = 0, ls = 0;
		int ind = 0;
		
		for(ind = 0; ind<prob.length; ind++)
		{
			ls = li + prob[ind]/F;

			if(p>=li && p<=ls)
			{
				break;
			}

			li = ls;
		}
		byte posicao = 0;
		switch (ind) {
		case 0:
			posicao = Formiga.OESTE;
			break;
		case 1:
			posicao = Formiga.LESTE;
			break;
		case 2:
			posicao = Formiga.NORTE;
			break;
		case 3:
			posicao = Formiga.SUL;
			break;

		default:
			break;
		}
		
		return posicao;
	}

	public void addLinhaParede(String[] linha) {
		Vector<Integer> mazeLinha = new Vector<Integer>();

		for (String token : linha) {
			if (token.length() > 0) {
				int posicao = token.indexOf("-");
				if (posicao > -1) {
					mazeLinha.add(MAZE_PAREDE);
				} else {
					mazeLinha.add(MAZE_VAZIO);
				}
				mazeLinha.add(MAZE_MAIS);
			}
		}
		maze.add(mazeLinha);
		mazeLinha.remove(mazeLinha.size() - 1);
	}

	public void clean() {
		maze.remove(maze.size() - 1);
		maze.remove(0);
	}

	public int getLimiteX() {
		return maze.get(0).size();
	}

	public int getLimiteY() {
		return maze.size();
	}

	public int getTamanho() {
		return tamanho;
	}

	public void print() {
		for (int i = 0; i < maze.size(); i++) {
			Vector<Integer> linha = maze.get(i);
			for (Integer value : linha) {
				System.out.print(value);
			}
			System.out.println();
		}
	}

	public int getInicialColuna() {
		return inicial_coluna;
	}

	public int getInicialLinha() {
		return inicial_linha;
	}

	public int getSaidaColuna() {
		return saida_coluna;
	}

	public int getSaidaLinha() {
		return saida_linha;
	}

	public boolean isValidMove(int i, int j, int movimento) {
		if ((i >= 0 && i < this.maze.size())
				&& (j >= 0 && j < this.maze.get(0).size())) {
			switch (movimento) {
			case Formiga.NORTE:
				if (i - 2 >= 0) {
					if (maze.get(i - 1).get(j) != MAZE_PAREDE) {
						return true;
					}
				}
				break;
			case Formiga.SUL:
				if (i + 2 <= this.maze.size()) {
					if (maze.get(i + 1).get(j) != MAZE_PAREDE) {
						return true;
					}
				}
				break;
			case Formiga.LESTE:
				if (j + 2 <= this.maze.get(0).size()) {
					if (maze.get(i).get(j + 1) != MAZE_PAREDE) {
						return true;
					}
				}
				break;
			case Formiga.OESTE:
				if (j - 2 >= 0) {
					if (maze.get(i).get(j - 1) != MAZE_PAREDE) {
						return true;
					}
				}
				break;
			}
		}

		return false;
	}

	public boolean hasNexPosition(Set<Byte> direcoes, int tabY, int tabX, byte direcao) {
		Set<Byte> nextPostions = new TreeSet<Byte>();
		for(byte i = 0; i<4; i++)
		{
			if(isValidMove(tabY, tabX, i))
			{
				nextPostions.add(i);
			}
			else
			{
				direcoes.add(i);
			}
		}
		return direcoes.size()<4;
	}

	public void limparVisitantes() {
		for(int i=0; i<visitantes.length; i++)
		{
			for(int j = 0; j<visitantes[i].length; j++)
			{
				if(visitantes[i][j]==null)
				{
					visitantes[i][j] = new TreeSet<Integer>();
				}
				else
				{
					visitantes[i][j].clear();
				}
			}
		}
	}
}
